﻿using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

///<summary>
/// Manage the spawns of ennemies
/// </summary>

enum LevelState
{
    Start,
    Wave,
    Pause,
    MainMenu
}

public class LevelMaster : MonoBehaviour
{
    public GameManager gameManager;
    public StatManager statManager;
    public EndLevelStat endLevelStat;
    public GameObject[] ennemyList;

    private Level level;
    private List<GameObject> turretList;
    private List<GameObject> ennemyAlive;
    private LevelState currentState;

    public Level Level
    {
        get { return level; }
        set { level = value; }
    }

    //Upgrades
    private float cashMultiplier;
    public float CashMultiplier
    {
        get { return cashMultiplier; }
        set { cashMultiplier = value; }
    }
    private float buildCostRatio;
    public float BuildCostRatio
    {
        get { return buildCostRatio; }
        set { buildCostRatio = value; }
    }

    //Time variables
    private float startLevelTime;
    private float startPauseTime;
    private float lastSpawnTime;
    private const float startDuration = 30;
    private const float betweenWaveDuration = 10;
    private const float betweenSpawnDuration = 0.3f;
    private bool accelerated = false;
    private float timeSpentAccelerating;

    //Spawn variable
    private int spawnInQueue;
    private int ennemyID;

    //stats
    private int ennemyKilled;

    //Player variables
    private int maxHealth = 10;
    public int MaxHealth
    {
        get { return maxHealth; }
        set { maxHealth = value; }
    }
    private int healthCount;
    private int cashCount;
    private int topOffset = 50;
    private int waveCount = 0;
    
	// Use this for initialization
	void Start ()
    {
        statManager = GameObject.FindGameObjectWithTag("StatManager").GetComponent<StatManager>();
        turretList = new List<GameObject>();
        ennemyAlive = new List<GameObject>();

        raz();
        currentState = LevelState.MainMenu;
        cashMultiplier = 1;
        BuildCostRatio = 1;
        ennemyKilled = 0;

        //Event listeners
        EventManager<Level>.AddListener("set level", onSetLevel);
        EventManager<GameObject>.AddListener("ennemy reach base", onEnemyReachBase);
        EventManager<GameObject>.AddListener("ennemy killed", onEnemyKilled);
        EventManager<EndLevelStat>.AddListener("end level", onLevelEnd);
        EventManager.AddListener("start level", onStartLevel);
	}

	// Update is called once per frame
    private void Update() 
    {
        switch (currentState)
        {
            case LevelState.Start:
                if (Time.time - startLevelTime > startDuration)
                {
                    currentState = LevelState.Wave;
                    lastSpawnTime = Time.time;
                }
                break;

            case LevelState.Wave:
                if(healthCount <= 0)
                {
                    raiseEndLevel(false);
                }
                if(Time.time - lastSpawnTime > betweenSpawnDuration)
                {
                    lastSpawnTime = Time.time;
                    if(spawnInQueue > 0)
                    {
                        ennemyAlive.Add(level.spawnNewEnnemy(ennemyList[ennemyID]));
                        spawnInQueue--;
                    }
                    else
                    {
                        if (level.waveIsOver())
                        {
                            if (ennemyAlive.Count == 0)
                            {
                                if (level.levelIsOver())
                                {
                                    raiseEndLevel(true);
                                    break;
                                }
                                startPauseTime = Time.time;
                                level.setNextWave();
                                currentState = LevelState.Pause;
                            }
                        }
                        else
                        {
                            Vector2 spawn = level.getNextSpawn();
                            ennemyID = (int)spawn.x;
                            spawnInQueue = (int)spawn.y;
                        }
                    }
                }
                break;

            case LevelState.Pause:
                if(Time.time - startPauseTime > betweenWaveDuration)
                {
                    currentState = LevelState.Wave;
                    lastSpawnTime = Time.time;
                }
                break;
            case LevelState.MainMenu:

                break;
        }
	}

    void OnGUI()
    {
        switch (currentState)
        {
            case LevelState.Start:
                if (GUI.Button(new Rect(200, Screen.height - 60, 100, 50), "Start wave"))
                {
                    waveCount = 1;
                    currentState = LevelState.Wave;
                }
                GUI.Label(new Rect(280, Screen.height - 60, 120, 50), "Level start in : \n" + (int)(startDuration - (Time.time - startLevelTime)));
                break;

            case LevelState.Wave:
                if (accelerated)
                {
                    timeSpentAccelerating += Time.deltaTime;
                    if (GUI.Button(new Rect(Screen.width - 100, 60, 75, 50), "Speed x1"))
                    {
                        Time.timeScale = 1;
                        accelerated = false;
                    }
                }
                else
                {
                    if (GUI.Button(new Rect(Screen.width - 100, 60, 75, 50), "Speed x3"))
                    {
                        Time.timeScale = 3;
                        accelerated = true;
                    }
                }
                break;

            case LevelState.Pause:
                if (GUI.Button(new Rect(200, Screen.height - 60, 100, 50), "Start wave"))
                {
                    waveCount++;
                    currentState = LevelState.Wave;
                    raiseXpGain((int)(betweenWaveDuration - (Time.time - startPauseTime)) * 10,  Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height - 60, 0)));
                }
                GUI.Label(new Rect(280, Screen.height - 60, 120, 50), "Level start in : \n" + (int)(betweenWaveDuration - (Time.time - startPauseTime)));
                break;
                
            case LevelState.MainMenu:

                break;
        }
        if(currentState != LevelState.MainMenu)
        {
            GUI.Label(new Rect(topOffset + 0 * Screen.width / 4, 0, 150, 75), "Wave : " + waveCount);
            GUI.Label(new Rect(topOffset + 2 * Screen.width / 4, 0, 150, 75), "Health : " + healthCount);
            GUI.Label(new Rect(topOffset + 3 * Screen.width / 4, 0, 150, 75), "Cash : " + cashCount);
        }
    }

    public void surrender()
    {
        clearBoard();
        raiseEndLevel(false);
        currentState = LevelState.MainMenu;
    }

    //Event listener function trigger
    void onStartLevel()
    {
        startLevelTime = Time.time;
        timeSpentAccelerating = 0;
        currentState = LevelState.Start;
    }

    void onSetLevel(Level nLevel)
    {
        raz();
        Level = nLevel;
    }

    public void onEnemyReachBase(GameObject ennemy)
    {
        healthCount--;
        ennemyAlive.Remove(ennemy);
    }

    public void onEnemyKilled(GameObject ennemy)
    {
        ennemyKilled++;
        ennemyAlive.Remove(ennemy);
        cashCount += (int)(ennemy.GetComponent<EnnemyBehaviour>().Income * cashMultiplier);
    }

    private void raiseEndLevel(bool win)
    {
        endLevelStat.setStat(ennemyKilled, (MaxHealth - healthCount), win, (timeSpentAccelerating / (Time.time - startLevelTime)), level);
        EventManager<EndLevelStat>.Raise("end level", endLevelStat);
    }

    private void raiseXpGain(int xp, Vector3 position)
    {
        EventManager<int, Vector3>.Raise("xp gain", xp, position);
    }

    private void onLevelEnd(EndLevelStat stat)
    {
        clearBoard();
        currentState = LevelState.MainMenu;
    }

    public bool canBuild(GameObject tower, GameObject placement)
    {
        return (canBuy(statManager.getBuildCost(tower.GetComponent<Turret.Turret>().getCurrentStat())) && placement.tag == "PlacementPlane_open");
    }

    public void build(GameObject tower, GameObject placement)
    {
        if (canBuild(tower, placement))
        {
            cashCount -= (int)(statManager.getBuildCost(tower.GetComponent<Turret.Turret>().getCurrentStat()) * BuildCostRatio);
            //Update the tag of the placementPlace to taken
            placement.tag = "PlacementPlane_taken";
            //Instantiate a turret chosen by the player
            GameObject newStruct = (GameObject)Instantiate(tower, placement.transform.position, Quaternion.identity);
            newStruct.transform.localEulerAngles.Set(newStruct.transform.localEulerAngles.x, UnityEngine.Random.Range(0, 360), newStruct.transform.localEulerAngles.z);
            newStruct.GetComponent<Turret.Turret>().setGhost(false);
            newStruct.GetComponent<Turret.Turret>().hideRadius();
            newStruct.GetComponent<Turret.Turret>().setPlacement(placement);
            turretList.Add(newStruct);
        }
    }

    public void sell(GameObject tower)
    {
        //Set the placement back to free
        tower.GetComponent<Turret.Turret>().getPlacement().tag = "PlacementPlane_open";
        cashCount += statManager.getBuildCost(tower.GetComponent<Turret.Turret>().getCurrentStat()) / 2;
        turretList.Remove(tower);
        Destroy(tower);
    }

    private void raz()
    {
        foreach (GameObject obj in ennemyAlive)
        {
            Destroy(obj);
        }
        healthCount = MaxHealth;
        cashCount = 300;
        currentState = LevelState.MainMenu;
        spawnInQueue = 0;
        Time.timeScale = 1;
        accelerated = false;
        ennemyKilled = 0;
    }

    private void clearBoard()
    {
        while (turretList.Count > 0)
        {
            sell(turretList[turretList.Count - 1]);
        }
        turretList.Clear();
    }

    public void ToggleBuildPanel(bool show)
    {
        //If the panel was open when clicking, it has to be closed
        Level.showPlacementPlace(show);
    }

    public bool canBuy(int price)
    {
        return (price <= cashCount);
    }

    public void spendChash(int price)
    {
        if(cashCount >= price)
            cashCount -= price; ;
    }
}
